<template>
  <div 
    class="canvas" 
    ref="canvasRef"
    @mousewheel="$event => handleMousewheelCanvas($event)"
    @mousedown="$event => handleClickBlankArea($event)"
    v-contextmenu="contextmenus"
    v-click-outside="removeEditorAreaFocus"
  >
    <ElementCreateSelection
      v-if="creatingElement"
      ref='createselection'
      @created="data => insertElementFromCreateSelection(data)"
    />
    <div 
      class="viewport-wrapper"
      :style="`
        width: ${canvasWidth * canvasScale}px;
        height: ${canvasHeight * canvasScale}px;
      `"
    >
      <!-- <div class="resize resizetop" @mousedown="mousedown($event, 'top')"></div> -->
      <div class="resize resizebot" @mouseover="mouseover=true" @mouseleave="mouseover=false" @mousedown="mousedown($event, 'bot')"></div>
      <div class="resize resizeLeft" @mouseover="mouseover=true" @mouseleave="mouseover=false" :style="`opacity:${mouseover? 1 : 0}`" @mousedown="mousedown($event, 'left')"></div>
      <div class="resize resizeRight" @mouseover="mouseover=true" @mouseleave="mouseover=false" :style="`opacity:${mouseover? 1 : 0}`" @mousedown="mousedown($event, 'right')"></div>
      <div class="operates">
        <AlignmentLine 
          v-for="(line, index) in alignmentLines" 
          :key="index" 
          :type="line.type" 
          :axis="line.axis" 
          :length="line.length"
        />
        <MultiSelectOperate 
          v-if="activeElementIdList.length > 1"
          :elementList="elementList"
          :scaleMultiElement="scaleMultiElement"
        />
        <Operate
          v-for="element in elementList" 
          :key="element.id"
          :elementInfo="element"
          :isSelected="activeElementIdList.includes(element.id)"
          :isActive="handleElementId === element.id"
          :isActiveGroupElement="activeGroupElementId === element.id"
          :isMultiSelect="activeElementIdList.length > 1"
          :rotateElement="rotateElement"
          :scaleElement="scaleElement"
          :openLinkDialog="openLinkDialog"
          :dragLineElement="dragLineElement"
        />
        <ViewportBackground />
      </div>

      <div 
        class="viewport" 
        ref="viewportRef"
        :style="{ transform: `scale(${canvasScale})` }"
      >
        <MouseSelection 
          v-if="mouseSelectionState.isShow"
          :top="mouseSelectionState.top" 
          :left="mouseSelectionState.left" 
          :width="mouseSelectionState.width" 
          :height="mouseSelectionState.height" 
          :quadrant="mouseSelectionState.quadrant"
        />      
        <EditableElement 
          v-for="(element, index) in elementList" 
          :key="element.id"
          :elementInfo="element"
          :elementIndex="index + 1"
          :isMultiSelect="activeElementIdList.length > 1"
          :selectElement="selectElement"
          :openLinkDialog="openLinkDialog"
        />
      </div>
    </div>

    <Modal
      v-model:visible="linkDialogVisible" 
      :footer="null" 
      centered
      :width="540"
      destroyOnClose
    >
      <LinkDialog @close="linkDialogVisible = false" />
    </Modal>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, onUnmounted, provide, ref, watch, watchEffect } from 'vue'
import { throttle } from 'lodash'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore, useKeyboardStore } from '@/store'
import { ContextmenuItem } from '@/components/Contextmenu/types'
import { PPTElement } from '@/types/slides'
import { AlignmentLineProps } from '@/types/edit'
import { removeAllRanges } from '@/utils/selection'
import { KEYS } from '@/configs/hotkey'

import useViewportSize from './hooks/useViewportSize'
import useMouseSelection from './hooks/useMouseSelection'
import useDropImageOrText from './hooks/useDropImageOrText'
import useRotateElement from './hooks/useRotateElement'
import useScaleElement from './hooks/useScaleElement'
import useSelectElement from './hooks/useSelectElement'
import useDragElement from './hooks/useDragElement'
import useDragLineElement from './hooks/useDragLineElement'
import useInsertFromCreateSelection from './hooks/useInsertFromCreateSelection'

import useDeleteElement from '@/hooks/useDeleteElement'
import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
import useSelectAllElement from '@/hooks/useSelectAllElement'
import useScaleCanvas from '@/hooks/useScaleCanvas'
import useScreening from '@/hooks/useScreening'
import useSlideHandler from '@/hooks/useSlideHandler'

import EditableElement from './EditableElement.vue'
import MouseSelection from './MouseSelection.vue'
import ViewportBackground from './ViewportBackground.vue'
import AlignmentLine from './AlignmentLine.vue'
import ElementCreateSelection from './ElementCreateSelection.vue'
import MultiSelectOperate from './Operate/MultiSelectOperate.vue'
import Operate from './Operate/index.vue'
import LinkDialog from './LinkDialog.vue'
import emitter, { EmitterEvents } from '@/utils/emitter'

export default defineComponent({
  name: 'editor-canvas',
  components: {
    EditableElement,
    MouseSelection,
    ViewportBackground,
    AlignmentLine,
    ElementCreateSelection,
    MultiSelectOperate,
    Operate,
    LinkDialog,
  },
  setup() {
    const mouseover = ref(false)
    const slidesStore = useSlidesStore()
    const { slides, slideIndex } = storeToRefs(slidesStore)
    const canvasRef = ref<HTMLElement>()
    const { viewportStyles } = useViewportSize(canvasRef)
    // console.log(viewportStyles,'viewportStyles')
    const canvasWidth = ref(viewportStyles.value.width)
    const canvasHeight = ref(viewportStyles.value.height)
    const canvasTop = ref()
    const canvasLeft = ref()
    
    let height = viewportStyles.value.height
    let width = viewportStyles.value.width
    let left = 0
    let top = 0
    setTimeout(() => {
      canvasTop.value = viewportStyles.value.top
      canvasLeft.value = viewportStyles.value.left
      left = viewportStyles.value.left
      top = viewportStyles.value.top
      // console.log(canvasTop,'canvasTop',viewportStyles.value.top)
    }, 0)
    // 
    watch( 
      () => viewportStyles.value,
      (val) => {
        canvasWidth.value = val.width
        canvasHeight.value = val.height
        canvasTop.value = val.top
        canvasLeft.value = val.left
        height = viewportStyles.value.height
        width = viewportStyles.value.width
        left = viewportStyles.value.left
        top = viewportStyles.value.top
      }
    )
    const mousedown = (event: MouseEvent, directive: string) => {
      event.stopPropagation()
      var starty = event.clientY
      var startx = event.clientX
      console.log("拖动按钮距离屏幕顶部的高度：", starty, startx)
      // var sb_bkx = startx - event.target.offsetLeft
      // var sb_bky = starty - event.target.offsetTop
      var ww = document.documentElement.clientWidth
      var wh = window.innerHeight
      if (event.preventDefault) {
        event.preventDefault()
      } else {
        event.returnValue = false
      }
      function fnMove(ev: MouseEvent) {
        var event = ev || window.event
        if (
          event.clientY < 0 ||
          event.clientX < 0 ||
          event.clientY > wh ||
          event.clientX > ww
        ) {
          return false
        }
        var endx = event.clientX - startx
        var endy = event.clientY - starty
        // console.log(endy,endx)
        directive === 'bot' ? canvasHeight.value = height + endy : ''
        directive === 'right' ? canvasWidth.value = width + endx : ''
        directive === 'left' ? canvasWidth.value = width - endx : ''
      }
      document.addEventListener('mousemove', fnMove, false)
      document.addEventListener('mouseup', () => {
        height = canvasHeight.value
        width = canvasWidth.value
        // top = canvasTop.value
        // left = canvasLeft.value
        mainStore.$patch({
          canvasWidth: canvasWidth.value,
          canvasHeight: canvasHeight.value
        })
        slidesStore.setViewportRatio(mainStore.canvasHeight/mainStore.canvasWidth)
        document.removeEventListener('mousemove', fnMove, false)
      })
    }
    const mouseRight = (event: MouseEvent) => {
      event.stopPropagation()     
    }
    const mainStore = useMainStore()
    const {
      activeElementIdList,
      activeGroupElementId,
      handleElementId,
      editorAreaFocus,
      showGridLines,
      creatingElement,
      canvasScale,
    } = storeToRefs(mainStore)
    const { currentSlide } = storeToRefs(useSlidesStore())
    const { ctrlKeyState, ctrlOrShiftKeyActive } = storeToRefs(useKeyboardStore())

    const viewportRef = ref<HTMLElement>()
    const alignmentLines = ref<AlignmentLineProps[]>([])

    const linkDialogVisible = ref(false)
    const openLinkDialog = () => linkDialogVisible.value = true

    watch(handleElementId, () => {
      mainStore.setActiveGroupElementId('')
    })

    const elementList = ref<PPTElement[]>([])
    const setLocalElementList = () => {
      elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []
    }
    watchEffect(setLocalElementList)

    

    useDropImageOrText(canvasRef)

    const { mouseSelectionState, updateMouseSelection } = useMouseSelection(elementList, viewportRef)

    const { dragElement } = useDragElement(elementList, alignmentLines)
    const { dragLineElement } = useDragLineElement(elementList)
    const { selectElement } = useSelectElement(elementList, dragElement)
    const { scaleElement, scaleMultiElement } = useScaleElement(elementList, alignmentLines)
    const { rotateElement } = useRotateElement(elementList, viewportRef)

    const { selectAllElement } = useSelectAllElement()
    const { deleteAllElements } = useDeleteElement()
    const { pasteElement } = useCopyAndPasteElement()
    const { enterScreening } = useScreening()
    const { updateSlideIndex } = useSlideHandler()

    // 点击画布的空白区域：清空焦点元素、设置画布焦点、清除文字选区
    const handleClickBlankArea = (e: MouseEvent) => {
      e.preventDefault()
      mainStore.setActiveElementIdList([])
      if (!ctrlOrShiftKeyActive.value) updateMouseSelection(e)
      if (!editorAreaFocus.value) mainStore.setEditorareaFocus(true)
      removeAllRanges()
    }

    // 移除画布编辑区域焦点
    const removeEditorAreaFocus = () => {
      if (editorAreaFocus.value) mainStore.setEditorareaFocus(false)
    }

    // 滚动鼠标
    const { scaleCanvas } = useScaleCanvas()
    const throttleScaleCanvas = throttle(scaleCanvas, 100, { leading: true, trailing: false })
    const throttleUpdateSlideIndex = throttle(updateSlideIndex, 300, { leading: true, trailing: false })

    const handleMousewheelCanvas = (e: WheelEvent) => {
      e.preventDefault()

      // 按住Ctrl键时：缩放画布
      if (ctrlKeyState.value) {
        if (e.deltaY > 0) throttleScaleCanvas('-')
        else if (e.deltaY < 0) throttleScaleCanvas('+')
      }
      // 上下翻页
      else {
        // mainStore.setCurrentTab(1)
        if (e.deltaY > 0) throttleUpdateSlideIndex(KEYS.DOWN)
        else if (e.deltaY < 0) throttleUpdateSlideIndex(KEYS.UP)
      }
    }

    // 开关网格线
    const toggleGridLines = () => {
      mainStore.setGridLinesState(!showGridLines.value)
    }

    // 在鼠标绘制的范围插入元素
    const { insertElementFromCreateSelection } = useInsertFromCreateSelection(viewportRef)
    const insertEle = (data: any) => {
      insertElementFromCreateSelection(data)

      // console.log(data)
    }
    onMounted(() => {
      emitter.on(EmitterEvents.INSERT_ELEMENT, insertEle)
    })
    onUnmounted(() => {
      emitter.off(EmitterEvents.INSERT_ELEMENT, insertEle)
    })
    const contextmenus = (): ContextmenuItem[] => {
      return [
        {
          text: '粘贴',
          subText: 'Ctrl + V',
          handler: pasteElement,
        },
        {
          text: '全选',
          subText: 'Ctrl + A',
          handler: selectAllElement,
        },
        {
          text: '网格线',
          subText: showGridLines.value ? '√' : '',
          handler: toggleGridLines,
        },
        {
          text: '重置当前页',
          handler: deleteAllElements,
        },
        { divider: true },
        {
          text: '从当前页演示',
          subText: 'Ctrl+F',
          handler: enterScreening,
        },
      ]
    }

    provide('slideScale', canvasScale)

    return {
      elementList,
      activeElementIdList,
      handleElementId,
      activeGroupElementId,
      canvasRef,
      viewportRef,
      viewportStyles,
      canvasScale,
      mouseSelectionState,
      currentSlide,
      creatingElement,
      alignmentLines,
      linkDialogVisible,
      openLinkDialog,
      handleClickBlankArea,
      removeEditorAreaFocus,
      insertElementFromCreateSelection,
      selectElement,
      rotateElement,
      scaleElement,
      dragLineElement,
      scaleMultiElement,
      handleMousewheelCanvas,
      contextmenus,
      insertEle,
      mousedown,
      canvasWidth,
      canvasHeight,
      canvasTop,
      canvasLeft,
      mouseRight,
      mouseover
    }
  },
})
</script>

<style lang="scss" scoped>
.resize {
  position: absolute;
  background: #656565;
  border-radius: 4px;
  cursor: row-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  &::after {
    content: '';
    box-sizing: border-box;
    position: absolute;
  }
  &:hover {
    opacity: 1;
    background: #1890ff;
  }
}
.resizebot,.resizetop {
  width: 65px;
  height: 13px;
  bottom: -15px;
  left: 50%;
  margin-left: -50px;
  opacity: 1;
  &::after {
    left: 20px;
    top: 4px;
    width: 23px;
    height: 5px;
    border-top: 1px solid #fff;
    border-bottom: 1px solid #fff;
  }
}
.resizetop {
  top: -13px;
  bottom: initial;
  opacity: 0;
}
.resizeRight,.resizeLeft {
  width: 13px;
  height: 65px;
  cursor: ew-resize;
  right: -13px;
  top: 50%;
  margin-top: -33px;
  &::after {
    left: 4px;
    top: 20px;
    height: 23px;
    width: 5px;
    border-left: 1px solid #fff;
    border-right: 1px solid #fff;
  }
}
.resizeLeft {
  left: -13px;
  right: initial;
}
.canvas {
  height: 100%;
  user-select: none;
  overflow: hidden;
  background-color: $centerbg;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.viewport-wrapper {
  position: absolute;
  box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1);
}
.viewport {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
}
</style>